/*************************************
 *  auth : sunqiang
 *  date : 20170118
 *  desp : config data opt
 *************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sn_config.h"

sn_config_t *
sn_create_config(u_char *file, sn_mempool_t *mpool) {
	sn_config_t         *conf;
	sn_config_section_t *sroot;

	/* 创建hash计算表 */
	sn_prepare_hash_table();

	sroot = sn_mpalloc(mpool, sizeof(sn_config_section_t));
	sroot->name = sn_mpalloc(mpool, strlen(CONF_ROOT_NODE_NAME)+1);
	memcpy(sroot->name ,CONF_ROOT_NODE_NAME, strlen(CONF_ROOT_NODE_NAME));
	sroot->next = NULL;
	sroot->type = CONF_LINK;
	sroot->node.key = sn_calcu_hash_string(sroot->name);

	conf = sn_mpalloc(mpool, sizeof(sn_config_t));
	conf->sroot = sroot;
	conf->mpool = mpool;

	conf->ntree = sn_mpalloc(mpool, sizeof(sn_rbtree_t));
	conf->ntree->sentinel = sn_mpalloc(mpool, sizeof(sn_rbtree_node_t));
	sn_rbtree_init(conf->ntree, conf->ntree->sentinel, sn_rbtree_init_insert_value);
	
	sn_rbtree_insert(conf->ntree, &sroot->node);

	if (sn_load_conf_file(sroot, file, conf)) {
		sn_conf_free_section_recursion(sroot);
		sn_mpfree(conf->ntree->sentinel);
		sn_mpfree(conf->ntree);
		sn_mpfree(conf);
		return NULL;
	}

	return conf;
}

void
sn_destory_config(sn_config_t *conf) {

	if (NULL == conf) {
		return ;
	}

	sn_conf_free_section_recursion(conf->sroot);
	sn_mpfree(conf->ntree->sentinel);
	sn_mpfree(conf->ntree);
	sn_mpfree(conf);

	return;
}

void 
sn_print_config(sn_config_t *conf) {
	sn_print_conf_section(conf->sroot, 0);
}

void 
sn_print_conf_section(sn_config_section_t *sect, int16_t n) {
	int16_t i;
	u_char *p;

	if (NULL == sect) {
		return;
	}

	p = sn_rfind_first_str(sect->name, ':');
	if (CONF_NODE == sect->type) {
		printf("%.*s%s = ", n , PRINT_LEVEL, p);
		for (i=0; i<sect->value.node->count; i++) {
			printf("%s;", sect->value.node->arr[i]);
		}
		printf("\n");
	} else if (CONF_LINK == sect->type) {
		printf("%.*s%s { \n", n , PRINT_LEVEL, p);
		sn_print_conf_section(sect->value.link, n+1);
		printf("%.*s}\n", n , PRINT_LEVEL);
	}

	if (sect->next) {
		sn_print_conf_section(sect->next, n);
	}
	return;
}

sn_conf_node_values_t *
sn_find_conf_node_value(sn_config_t *conf, u_char *sname) {
	sn_config_section_t  *sect;

	sect = sn_find_conf_section(conf, sname);
	if (NULL == sect) {
		return NULL;
	}

	if (CONF_NODE != sect->type) {
		return NULL;
	}

	return sect->value.node;
}

sn_config_section_t *
sn_find_conf_section(sn_config_t *conf, u_char *sname) {
	int16_t              len;
	u_char               *fname;
	unsigned long        key;
	sn_config_section_t  *sect;

	if (NULL == sname) {
		fname = CONF_ROOT_NODE_NAME;
	} else {
		len = strlen(sname) + strlen(CONF_ROOT_NODE_NAME) + 2;
		fname = sn_mpalloc(conf->mpool, len);
		sprintf(fname, "%s:%s", CONF_ROOT_NODE_NAME, sname);
	}

	key = sn_calcu_hash_string(fname);
	sect = sn_rbtree_lookup(conf->ntree->root, conf->ntree->sentinel, key);

	sn_mpfree(fname);
	
	return sect;
}


int32_t 
sn_load_conf_file(sn_config_section_t *sect, u_char *file, sn_config_t *conf) {
	FILE                    *fp;
	u_char                  *p;
	u_char                  *line;
	u_char                  path[CONF_FILE_PATH_LEN];
	u_char                  newfile[CONF_FILE_PATH_LEN];
	int16_t                 pushnode=0;
	int16_t                 n=0;
	int16_t                 level=0;
	int16_t                 rlevel;
	int32_t                 done;
	sn_str_t                up_key;
	sn_mempool_t            *mpool;
	sn_config_kv_t          kv;
	sn_config_section_t     *nsect;
	sn_config_section_t     *freesect;
	sn_conf_load_section_t  *loadsect;
	sn_conf_load_section_t  *loadnewsect;

	mpool = conf->mpool;
	freesect = sect;

	sn_str_set(&up_key, sect->name);

	memset(path, 0x00, CONF_FILE_PATH_LEN);
	memcpy(path, file, strlen(file));
	sn_lfind_last_mchar_str(path, '/');

	fp = fopen(file, "r+");
	if(NULL == fp) {
		return -1;
	}

	line = sn_mpalloc(mpool, CONF_FILE_LINE_LEN+1);
	if (NULL == line) {
		return -1;
	}

	loadsect = sn_mpalloc(mpool, sizeof(sn_conf_load_section_t));
	sn_queue_init(&loadsect->queue);
	loadsect->sect = sect;

	kv.type = CONF_OPT_COMM;
	while (fgets(line, CONF_FILE_LINE_LEN, fp)) {
		n++;
		p = line;

line_anay:
		p = sn_ltrim_mptr_str(p, CONF_FILE_LINE_TRIM);
		sn_note_mchar_str(p, CONF_FILE_LINE_NOTE);
		if (!*p) {
			continue;
		}


		rlevel = level;
		done = sn_get_conf_line(p, &up_key, &kv, &rlevel, mpool);
		if (0 > done || 0 > rlevel) {
			printf("config[%s][%d] error\n", p, n);
			sn_mpfree(line);
			sn_conf_free_loadsect(loadsect);
			sn_conf_free_section(freesect);
			fclose(fp);
			return -1;
		}
		//printf("level[%d].rlevel[%d].type[%d].p[%s]\n", level, rlevel, kv.type, p);
		
		if(level == rlevel) {
			if (CONF_OPT_COMM == kv.type) {
				nsect = sn_mpalloc(mpool, sizeof(sn_config_section_t));
				nsect->name = kv.key;
				nsect->next = NULL;
				nsect->type = CONF_NODE;
				sn_conf_section_build(nsect, conf, kv.val);

				if (pushnode) {
					sect->next = nsect;
				} else {
					sect->value.link = nsect;
				}
				pushnode = 1;
				sect = nsect;

				up_key.len = sn_lfind_last_cursor_str(nsect->name, ':');
				up_key.data = nsect->name;

			} else if (CONF_OPT_INCL == kv.type) {
				nsect = sn_mpalloc(mpool, sizeof(sn_config_section_t));
				nsect->name = kv.key;
				nsect->next = NULL;
				nsect->type = CONF_LINK;

				sn_conf_section_build(nsect, conf, NULL);
				if (sn_load_conf_file(nsect, kv.val, conf)) {
					printf("config file[%s] error\n", kv.val);
					sn_mpfree(line);
					sn_conf_free_loadsect(loadsect);
					sn_conf_free_section(freesect);
					fclose(fp);

					return -1;
				}

				if (pushnode) {
					sect->next = nsect;
				} else {
					sect->value.link = nsect;
				}
				pushnode = 1;
				sect = nsect;

				up_key.len = sn_lfind_last_cursor_str(nsect->name, ':');
				up_key.data = nsect->name;
			} else if (CONF_OPT_LOCA == kv.type) {
				printf("read location line[%s][%d] error\n", line, n);
				sn_mpfree(line);
				sn_conf_free_loadsect(loadsect);
				sn_conf_free_section(freesect);
				fclose(fp);

				return -1;
			} else if (CONF_OPT_INCL_VAL == kv.type) {
				memset(newfile, 0x00, CONF_FILE_PATH_LEN);
				sprintf(newfile, "%s/%s", path, kv.val);

				if (sn_load_conf_file(sect, newfile, conf)) {
					printf("config file[%s] error\n", kv.val);
					sn_mpfree(line);
					sn_conf_free_loadsect(loadsect);
					sn_conf_free_section(freesect);
					fclose(fp);

					return -1;
				}
			} else if (CONF_OPT_LOCA_VAL == kv.type) {
				sn_conf_section_insert_val(sect, conf, kv.val);
			}

		} else if(level > rlevel) {
			sect = loadsect->sect;

			loadnewsect = sn_queue_data(loadsect->queue.prev, sn_conf_load_section_t, queue);

			sn_mpfree(loadsect);
			loadsect = loadnewsect;

			kv.type = CONF_OPT_COMM;

			pushnode = 1;
			up_key.len = sn_lfind_last_cursor_str(sect->name, ':');
			up_key.data = sect->name;
		} else if (level < rlevel) {
			if (CONF_OPT_COMM == kv.type) {
				nsect = sn_mpalloc(mpool, sizeof(sn_config_section_t));
				nsect->name = kv.key;
				nsect->next = NULL;
				nsect->type = CONF_LINK;
				sn_conf_section_build(nsect, conf, NULL);

				if (pushnode) {
					sect->next = nsect;
				} else {
					sect->value.link = nsect;
				}

				pushnode = 0;
				sect = nsect;

				up_key.len = strlen(nsect->name);
				up_key.data = nsect->name;
			}else if (CONF_OPT_INCL == kv.type) {
				kv.type = CONF_OPT_INCL_VAL;

				nsect = sn_mpalloc(mpool, sizeof(sn_config_section_t));
				nsect->name = kv.key;
				nsect->next = NULL;
				nsect->type = CONF_LINK;

				sn_conf_section_build(nsect, conf, NULL);

				if (pushnode) {
					sect->next = nsect;
				} else {
					sect->value.link = nsect;
				}
				pushnode = 1;
				sect   = nsect;

			} else if (CONF_OPT_LOCA == kv.type) {
				kv.type = CONF_OPT_LOCA_VAL;

				nsect = sn_mpalloc(mpool, sizeof(sn_config_section_t));
				nsect->name = kv.key;
				nsect->next = NULL;
				nsect->type = CONF_NODE;

				sn_conf_section_build(nsect, conf, NULL);

				if (pushnode) {
					sect->next = nsect;
				} else {
					sect->value.link = nsect;
				}
				pushnode = 1;
				sect   = nsect;

			} else {
				printf("kv.type error[%d]\n", kv.type);
				sn_mpfree(line);
				sn_conf_free_loadsect(loadsect);
				sn_conf_free_section(freesect);
				fclose(fp);
				return -1;
			}
			
			loadnewsect = sn_mpalloc(mpool, sizeof(sn_conf_load_section_t));
			loadnewsect->sect = nsect;
			sn_queue_insert_after(&loadsect->queue, &loadnewsect->queue);
			loadsect = loadnewsect;
		}

		level = rlevel;
		p += done;
		if (p && *p) {
			goto line_anay;
		}
	}

	sn_mpfree(line);
	sn_conf_free_loadsect(loadsect);
	fclose(fp);

	return 0;
}

int32_t
sn_get_conf_line(u_char *src, sn_str_t *up_key, sn_config_kv_t *kv, 
		int16_t *level, sn_mempool_t *mpool) {
	int16_t  len;
	u_char   *msrc;
	u_char   *div;
	u_char   *p;
	u_char   *chk;
	u_char   *key=NULL;
	u_char   *val=NULL;

	msrc = src;
	p = msrc;
	if (CONF_OPT_INCL_VAL == kv->type || CONF_OPT_LOCA_VAL == kv->type) {
		val = p;
	}

	while(p && *p) {
		if ((' ' == *p || ':' == *p || '=' == *p || '{' == *p) && '\\' != *(p-1)) {

			/* 对非分割的空格符过滤 */
			key = p;
			if (' ' == *p) {
				chk = p+1;
				while(chk && *chk) {
					if (' ' == *chk) {
						chk++;
						continue;
					} else if ('{' == *chk || '=' == *chk || ':' == *chk) {
						p = chk;
					}
					break;
				}
			}
			div = p;
			p = sn_ltrim_mptr_str(++p, " ");

			/* 对取到的key值做判断，如果是include location 这样的标签，则下一块才是key */
			len = key-msrc;
			if (0 == sn_str_cmp(msrc, len, CONF_FILE_INCLUDE_FILE, strlen(CONF_FILE_INCLUDE_FILE))) {
				kv->type = CONF_OPT_INCL;
				msrc = p;
				continue;
			} else if (0 == sn_str_cmp(msrc, len, CONF_FILE_MULT_VALUES, strlen(CONF_FILE_MULT_VALUES))) {
				kv->type = CONF_OPT_LOCA;
				msrc = p;
				continue;
			} 


			/* 将获取的key添加到键值对中 */
			kv->key = sn_mpalloc(mpool, up_key->len+len+2);
			if (NULL == kv->key) {
				printf("alloc conf key error\n");
				return -1;
			}
			sprintf(kv->key, "%.*s:%.*s", up_key->len, up_key->data, len, msrc);

			if ('{' == *div) {
				(*level)++;
				p = div;
				break;
			} else {
				val = p;
			}
			continue;

		} else if(';' == *p && '\\' != *(p-1)) {
			break;
		} else if('}' == *p && '\\' != *(p-1)) {
			(*level)--;
			break;
		}
		p++;
	}

	if ((p && ';' == *p) && NULL == val) {
		return -1;
	} else if(p && !*p) {
		return -1;
	} else if ((p && *p) && val) {
		len = p-val;
		kv->val = sn_mpalloc(mpool, len+1);
		if (NULL == kv->val) {
			printf("alloc conf val error\n");
			sn_mpfree(kv->key);
			return -1;
		}
		memcpy(kv->val, val, len);
	}

	return p+1-src;
}

void 
sn_conf_section_build(sn_config_section_t *sect, sn_config_t *conf, u_char *val) {
	sn_conf_node_values_t       *vnode;

	sect->node.key = sn_calcu_hash_string(sect->name);
	sn_rbtree_insert(conf->ntree, &sect->node);

	if (CONF_NODE == sect->type) {
		vnode = sn_mpalloc(conf->mpool, sizeof(sn_conf_node_values_t));
		vnode->max   = CONF_NODE_VALUES_CNT;
		vnode->count = 0;
		vnode->arr = sn_mpalloc(conf->mpool, sizeof(u_char *) * CONF_NODE_VALUES_CNT);

		if (val) {
			vnode->arr[0] = val;
			vnode->count++;
		}
		sect->value.node = vnode;
	} else if (CONF_LINK == sect->type) {
		sect->value.link = NULL;
	}
}

void 
sn_conf_section_insert_val(sn_config_section_t *sect, sn_config_t *conf, void *val) {
	int16_t                size;
	u_char                 **oarr;
	sn_conf_node_values_t  *vnode;

	if (CONF_NODE == sect->type) {

		vnode = sect->value.node;
		if (vnode->max <= vnode->count) {
			size = vnode->max * 2;
			oarr = vnode->arr;

			vnode->max = size;
			*(vnode->arr) = sn_mpalloc(conf->mpool, sizeof(u_char *) * size);

			for (size=0; size<vnode->count; size++) {
				vnode->arr[size] = oarr[size];
			}

			sn_mpfree(oarr);
		}

		size = vnode->count;
		vnode->arr[size] = (u_char *)val;
		vnode->count++;
	} else if (CONF_LINK == sect->type) {
		sect->value.link = (sn_config_section_t *)val;
	}
}

void 
sn_conf_free_section(sn_config_section_t *sect) {
	if (NULL == sect || NULL == sect->next) {
		return ;
	}

	sn_conf_free_section_recursion(sect->next);
	sect->next = NULL;
}

void 
sn_conf_free_section_recursion(sn_config_section_t *sect) {

	if (NULL == sect) {
		return;
	}

	if (sect->next) {
		sn_conf_free_section_recursion(sect->next);
	}

	if (CONF_LINK == sect->type) {
		sn_conf_free_section_recursion(sect->value.link);
	}
	sn_conf_free_section_kv(sect);

	sn_mpfree(sect);

	return;
}

void
sn_conf_free_section_kv(sn_config_section_t *sect) {

	if (NULL == sect) {
		return;
	}

	sn_mpfree(sect->name);
	if (CONF_NODE == sect->type) {
		if (sect->value.node) {
			while (sect->value.node->count) {
				sn_mpfree(sect->value.node->arr[sect->value.node->count--]);
			}
			sn_mpfree(sect->value.node->arr);
			sn_mpfree(sect->value.node);
		}
	}

	return;
}

void 
sn_conf_free_loadsect(sn_conf_load_section_t *loadsect) {
	sn_conf_load_section_t   *begin;
	sn_conf_load_section_t   *next;

	if (NULL == begin) {
		return;
	}

	begin = loadsect;
	loadsect = sn_queue_data(begin->queue.next, sn_conf_load_section_t, queue);

	while (&begin->queue != &loadsect->queue) {
		next = sn_queue_data(loadsect->queue.next, sn_conf_load_section_t, queue);
		sn_mpfree(loadsect);
		loadsect = next;
	}

	sn_mpfree(begin);

	return;
}
